home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / interapplication comm / ae interaction sample / sender.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  20.1 KB  |  555 lines

  1. /*
  2.     File:        sender.c
  3.  
  4.     Contains:    Sender and Reciever are simple AppleEvent programs that demonstrate     
  5.                 all the permutations of interaction levels for sending    
  6.                 and recieving APpleEvents.
  7.                  Have fun with them.
  8.                  P.S. This also uses PBCatSearch, so I've done the typing for you if you've wanted
  9.                 to use this call but have been confused by the param block    
  10.  
  11.     Written by:     
  12.  
  13.     Copyright:    Copyright © 1984-1999 by Apple Computer, Inc., All Rights Reserved.
  14.  
  15.                 You may incorporate this Apple sample source code into your program(s) without
  16.                 restriction. This Apple sample source code has been provided "AS IS" and the
  17.                 responsibility for its operation is yours. You are not permitted to redistribute
  18.                 this Apple sample source code as "Apple sample source code" after having made
  19.                 changes. If you're going to re-distribute the source, we require that you make
  20.                 it clear in the source that the code was descended from Apple sample source
  21.                 code, but that you've made changes.
  22.  
  23.     Change History (most recent first):
  24.                 7/20/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  25.                 
  26.  
  27. */
  28.  
  29.  
  30. #include <Types.h>
  31. #include <memory.h>
  32. #include <Packages.h>
  33. #include <Errors.h>
  34. #include <quickdraw.h>
  35. #include <fonts.h>
  36. #include <dialogs.h>
  37. #include <windows.h>
  38. #include <menus.h>
  39. #include <events.h>
  40. #include <OSEvents.h>
  41. #include <Desk.h>
  42. #include <diskinit.h>
  43. #include <OSUtils.h>
  44. #include <resources.h>
  45. #include <toolutils.h>
  46. #include <AppleEvents.h>
  47. #include <EPPC.h>
  48. #include <GestaltEqu.h>
  49. #include <PPCToolbox.h> 
  50. #include <Processes.h>
  51. /* prototypes */
  52. void DoDiskEvents(long dinfo);                              /* hi word is error code, lo word is drive number */
  53. void DrawMain(WindowPtr drawIt);
  54. Boolean DoSelected(long val);
  55. pascal Boolean idleProc(EventRecord *eventIn, long *sleep, RgnHandle *mouseRgn);
  56. void InitAEStuff(void);
  57. void DoHighLevel(EventRecord *AERecord);
  58. void DoDaCall(MenuHandle themenu, long theit);
  59.      OSErr AEOpenHandler(AppleEvent *messagein, AppleEvent *reply, long refIn);
  60.      OSErr AEOpenDocHandler(AppleEvent *messagein, AppleEvent *reply, long refIn);
  61.      OSErr AEPrintHandler(AppleEvent *messagein, AppleEvent *reply, long refIn);
  62.      OSErr AEQuitHandler(AppleEvent *messagein, AppleEvent *reply, long refIn);
  63.      OSErr AEReplyHandler(AppleEvent *messagein, AppleEvent *reply, long refIn);
  64. void SendSimple(void);
  65. OSErr FindReciever(AEDesc *theAddress);
  66. #define kSwitchItem 5
  67. #define kReplyItem 7
  68. short gSendInteractArray[4] = 
  69. {
  70.     nil, kAENeverInteract, kAECanInteract, kAEAlwaysInteract, 
  71. };
  72.  
  73.  
  74.  
  75. short replyLevels[] = 
  76. {
  77.     kAENoReply, kAEWaitReply,kAEQueueReply
  78. };
  79.  
  80.  
  81. short replyValue = 0;
  82.  
  83. short gInteract = 1;
  84. short gSwitchLayer = false;
  85. LaunchParamBlockRec launchThis;
  86.  
  87. #define kMBarID 128
  88. #define kAppleMenu 128
  89. #define kFileMenu 129
  90. #define kEditMenu 130
  91. #define kToolsMenu 131
  92. #define kSendButton 128
  93. #define kResumeMask             1       /* bit of message field for resume vs. suspend */
  94. #define kBadCombo 129
  95. #define kNoFind 130
  96. #define kSearch 200
  97. MenuHandle gAppleMenuHandle, gFileMenuHandle, gEditMenuHandle, gToolMenuHandle;
  98. Handle gMymenu;                                             /* my menu bar handle */
  99. ControlHandle sendButton;
  100.  
  101. AEAddressDesc targetAddress;                                /* address of the person to get the data from */
  102.  
  103.  
  104. #define kSimpleEvent 'SIMP'
  105. #define kSimpleClass 'Simp'
  106. Boolean gQuit, gInBackground;
  107. EventRecord gERecord;
  108. AEDesc gTheAddress;
  109. AEIdleUPP gAEIdleUPP;
  110. WindowPtr myWindow;
  111. void main()
  112. {
  113.     WindowPtr twindow;
  114.     ControlHandle returnedControl;
  115.     MaxApplZone();
  116.     InitGraf((Ptr)&qd.thePort);
  117.     InitFonts();
  118.     InitWindows();
  119.     InitMenus();
  120.     TEInit();
  121.     InitDialogs(nil);
  122.     InitCursor();
  123.     
  124.     InitAEStuff();
  125.     /* set up my menu junk */
  126.     gMymenu = GetNewMBar(kMBarID);
  127.     SetMenuBar(gMymenu);
  128.     gAppleMenuHandle = GetMenuHandle(kAppleMenu);
  129.     gFileMenuHandle = GetMenuHandle(kFileMenu);
  130.     gEditMenuHandle = GetMenuHandle(kEditMenu);
  131.     gToolMenuHandle = GetMenuHandle(kToolsMenu);
  132.     CheckItem(gToolMenuHandle, gInteract, true);
  133.     CheckItem(gToolMenuHandle, replyValue+kReplyItem, true);
  134.     AppendResMenu(gAppleMenuHandle, 'DRVR');
  135.  
  136.     
  137.    
  138.     /* this finds and launches the receiver */
  139.     if(FindReciever(&gTheAddress) != noErr){
  140.         StopAlert(kNoFind,nil);
  141.         ExitToShell();}
  142.     DrawMenuBar();
  143.     myWindow = GetNewWindow(128, nil, (WindowPtr)-1);
  144.     GetNewControl(kSendButton, myWindow);
  145.     do {
  146.         WaitNextEvent(everyEvent, &gERecord, 30, nil);
  147.         switch (gERecord.what) {
  148.             
  149.             case nullEvent:
  150.                 /* no nul processing in this sample */
  151.                 break;
  152.             case updateEvt:
  153.                 DrawMain((WindowPtr)gERecord.message);      /* draw whatever window needs an update */
  154.                 break;
  155.             case mouseDown:
  156.                 /* first see where the hit was */
  157.                 switch (FindWindow(gERecord.where, &twindow)) {
  158.                     
  159.                     case inDesk:                            /* if they hit in desk, then the process manager */
  160.                         break;                              /* will switch us out, we don't need to do anything */
  161.                     case inMenuBar:
  162.                         DoSelected(MenuSelect(gERecord.where));
  163.                         break;
  164.                         
  165.                     case inSysWindow:
  166.                         /* pass to the system */
  167.                         SystemClick(&gERecord, twindow);
  168.                         break;
  169.                     case inContent:
  170.                         GlobalToLocal(&gERecord.where);
  171.                         /* track my button as needed */
  172.                         if (FindControl(gERecord.where, twindow, &returnedControl)) {
  173.                             if (TrackControl(returnedControl, gERecord.where, nil)) {
  174.                                 SendSimple();
  175.                             }
  176.                         }
  177.                         break;
  178.                     case inDrag:
  179.                         if (twindow == FrontWindow())
  180.                             DragWindow(twindow, gERecord.where, &qd.screenBits.bounds);
  181.                         break;
  182.                     case inGrow:
  183.                     case inGoAway:
  184.                         /* don't care */
  185.                         break;
  186.                         
  187.                 }
  188.             case mouseUp:
  189.                 /* don't care */
  190.                 break;
  191.                 /* same action for key or auto key */
  192.             case keyDown:
  193.             case autoKey:
  194.                 if (gERecord.modifiers & cmdKey)
  195.                     DoSelected(MenuKey(gERecord.message & charCodeMask));
  196.                 break;
  197.             case keyUp:
  198.                 /* don't care */
  199.                 break;
  200.             case diskEvt:
  201.                 /* I don't do anything special for disk events, this just passes them */
  202.                 /* to a function that checks for an error on the mount */
  203.                 DoDiskEvents(gERecord.message);
  204.                 break;
  205.             case activateEvt:
  206.                 if (gERecord.modifiers & activeFlag)
  207.                     DrawMain((WindowPtr)gERecord.message);
  208.                 break;
  209.             case networkEvt:
  210.                 /* don't care */
  211.                 break;
  212.             case driverEvt:
  213.                 /* don't care */
  214.                 break;
  215.             case app4Evt:
  216.                 switch ((gERecord.message >> 24) & 0x0FF) {     /* high byte of message */
  217.                     case suspendResumeMessage:              /* suspend/resume is also an activate/deactivate */
  218.                         gInBackground = (gERecord.message & kResumeMask) == 0;
  219.                         break;
  220.                 }
  221.                 break;
  222.             default:
  223.                 break;
  224.                 /* This dispatches high level events (AppleEvents, for example) */
  225.                 /* to our dispatch routine.  This is NEW in the event loop for */
  226.                 /* System 7 */
  227.             case kHighLevelEvent:
  228.                 DoHighLevel(&gERecord);
  229.                 break;
  230.                 
  231.         }
  232.     } while (gQuit != true);
  233.     
  234.     
  235. }
  236.  
  237. /* DoDaCall opens the requested DA.  It's here as a seperate routine if you'd */
  238. /* like to perform some action or just know when a DA is opened in your */
  239. /* layer.  Can be handy to track memory problems when a DA is opened */
  240. /* with an Option-open */
  241. void DoDaCall(MenuHandle themenu, long theit)
  242. {
  243.     long qq;
  244.        Str255 DAname;
  245.     GetMenuItemText(themenu, theit, DAname);
  246.     qq = OpenDeskAcc(DAname);
  247. }
  248.  
  249. /* end DoDaCall */
  250.  
  251. /* DoDiskEvents just checks the error code from the disk mount, */
  252. /* and puts up the 'Format' dialog (through DIBadMount) if need be */
  253. /* You can do much more here if you care about what disks are */
  254. /* in the drive */
  255. void DoDiskEvents(long dinfo)                               /* hi word is error code, lo word is drive number */
  256. {
  257.     short hival, loval, tommy;
  258.     Point fredpoint =  {
  259.         40, 40
  260.     };
  261.     hival = HiWord(dinfo);
  262.     loval = LoWord(dinfo);
  263.     if (hival != noErr)                                     /* something happened */ {
  264.         tommy = DIBadMount(fredpoint, dinfo);
  265.     }
  266. }
  267.  
  268. /* draws my window.  Pretty simple */
  269. void DrawMain(WindowPtr drawIt)
  270. {
  271.     BeginUpdate(drawIt);
  272.     SetPort(drawIt);
  273.     DrawControls(drawIt);
  274.     EndUpdate(drawIt);
  275. }
  276.  
  277. /* my menu action taker.  It returns a Boolean which I usually ignore, but it */
  278. /* mught be handy someday */
  279. Boolean DoSelected(long val)
  280. {
  281.     short loval, hival;
  282.     Boolean temp = false;
  283.     loval = LoWord(val);
  284.     hival = HiWord(val);
  285.     
  286.     switch (hival) {                                        /* switch off the menu number selected */
  287.         case kAppleMenu:                                    /* Apple menu */
  288.             if (loval != 1) {                               /* if this was not About, it's a DA */
  289.                 DoDaCall(gAppleMenuHandle, loval);
  290.             } else {
  291.                 Alert(128, nil);                            /* do about box */
  292.             }
  293.             break;
  294.         case kFileMenu:                                     /* File menu */
  295.             gQuit = true;                                   /* only  item */
  296.             break;
  297.         case kEditMenu:
  298.             /* edit menu junk */
  299.             /* don't care */
  300.             break;
  301.             /* This is the interaction level menu, sets checks and variables */
  302.         case kToolsMenu:
  303.             if (loval == kSwitchItem) {
  304.                if (gSwitchLayer) gSwitchLayer = false;
  305.                  else gSwitchLayer = kAECanSwitchLayer;
  306.  
  307.                 CheckItem(gToolMenuHandle, kSwitchItem, gSwitchLayer);
  308.             } else {
  309.                 if (loval >= kReplyItem) {
  310.                     if(loval != replyValue){
  311.                     
  312.                     CheckItem(gToolMenuHandle, replyValue+kReplyItem, false);
  313.                     CheckItem(gToolMenuHandle,loval,true);
  314.                     replyValue = loval - kReplyItem;}
  315.                 } else {
  316.                     if (gInteract != loval) {
  317.                         CheckItem(gToolMenuHandle, gInteract, false);
  318.                         CheckItem(gToolMenuHandle, loval, true);
  319.                         gInteract = loval;
  320.                     }
  321.                 }
  322.             }
  323.             break;
  324.     }
  325.     HiliteMenu(0);
  326.     return temp;
  327. }
  328.  
  329. /* InitAEStuff installs my appleevent handlers */
  330. void InitAEStuff(void)
  331. {    
  332.     OSErr aevtErr = noErr;
  333.     long aLong = 0;
  334.     Boolean gHasAppleEvents = false;
  335.     /* Check this machine for AppleEvents.  If they are not here (ie not 7.0)
  336.     *   then we exit */
  337.     gHasAppleEvents = (Gestalt(gestaltAppleEventsAttr, &aLong) == noErr);
  338.     /* The following series of calls installs all our AppleEvent Handlers.
  339.     *   These handlers are added to the application event handler list that 
  340.     *   the AppleEvent manager maintains.  So, whenever an AppleEvent happens
  341.     *   and we call AEProcessEvent, the AppleEvent manager will check our
  342.     *   list of handlers and dispatch to it if there is one.
  343.     */
  344.     if (gHasAppleEvents) {
  345.          aevtErr = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, 
  346.              NewAEEventHandlerProc(AEOpenHandler),0, false);
  347.              if (aevtErr)  ExitToShell();
  348.  
  349.          aevtErr = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, 
  350.              NewAEEventHandlerProc(AEOpenDocHandler),0, false);
  351.              if (aevtErr)  ExitToShell();
  352.  
  353.          aevtErr = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, 
  354.              NewAEEventHandlerProc(AEQuitHandler), 0, false);
  355.              if (aevtErr)  ExitToShell();
  356.  
  357.          aevtErr = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, 
  358.              NewAEEventHandlerProc(AEPrintHandler),0, false);
  359.              if (aevtErr)  ExitToShell();
  360.  
  361.          aevtErr = AEInstallEventHandler(kCoreEventClass, kAEAnswer, 
  362.              NewAEEventHandlerProc(AEReplyHandler),0, false);
  363.              if (aevtErr)  ExitToShell();
  364.  
  365.          if (aevtErr)  ExitToShell();
  366.          
  367.          /* create a UPP for the AppleEvent idle proc */
  368.          gAEIdleUPP = NewAEIdleProc(idleProc);
  369.  
  370.        } 
  371.     else ExitToShell();
  372.     
  373. }
  374. /* end InitAEStuff */
  375.  
  376.  
  377. /* I'm not doing error handling in this sample for clarities sake, you should. Hah, */
  378. /* easy for me to say, huh? */
  379. void DoHighLevel(EventRecord *AERecord)
  380. {
  381.     
  382.     AEProcessAppleEvent(AERecord);
  383.     
  384. }
  385.  
  386. /* end DoHighLevel */
  387.  
  388. /* This is the standard Open Application event.  */
  389.      OSErr AEOpenHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  390. {
  391. #pragma unused (messagein,reply,refIn)
  392.     /* we of course don't do anything here in this simple app */
  393.     return(noErr);
  394. }
  395.  
  396. /* end AEOpenHandler */
  397.  
  398. /* Open Doc, opens our documents.  Remember, this can happen at application start AND */
  399. /* anytime else.  If your app is up and running and the user goes to the desktop, hilites one */
  400. /* of your files, and double-clicks or selects Open from the finder File menu this event */
  401. /* handler will get called. Which means you don't do any initialization of globals here, or */
  402. /* anything else except open then doc.  */
  403. /* SO-- Do NOT assume that you are at app start time in this */
  404. /* routine, or bad things will surely happen to you. */
  405.  
  406.      OSErr AEOpenDocHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  407. {
  408. #pragma unused (messagein,reply, refIn)
  409.     /* we of course don't do anything here */
  410.     return(errAEEventNotHandled);                           /* we have no docs, so no odoc events should come to us */
  411. }
  412.  
  413.      OSErr AEPrintHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  414. {                                                           /* no printing handler in yet, so we'll ignore this */
  415.     /* the operation is functionally identical to the ODOC event, with the additon */
  416.     /* of calling your print routine.  */
  417. #pragma unused (messagein,reply,refIn)
  418.     /* we of course don't do anything here */
  419.     return(errAEEventNotHandled);                           /* we have no docs, so no pdoc events should come to us */
  420. }
  421.  
  422. /* Standard Quit event handler, to handle a Quit event from the Finder, for example.  */
  423. /* ••••• DO NOT CALL EXITTOSHELL HERE ••••• or you will never have a happy life.  */
  424.      OSErr AEQuitHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  425. {
  426. #pragma unused (messagein,refIn,reply)
  427.     
  428.     /* prepQuit sets the Stop flag for us.  It does _NOT_ quit, you */
  429.     /* should NEVER quit from an AppleEvent handler.  Calling */
  430.     /* ExitToShell here would blow things up */
  431.     gQuit = true;
  432.     return(noErr);
  433. }
  434. /* ReplyHandler is used when I've used QueueReply, which means that the */
  435. /* reply will come in through my event loop */
  436.      OSErr AEReplyHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  437. {
  438. #pragma unused (messagein,refIn,reply)
  439. return(noErr);
  440. }
  441. /* SendSimple sends our simple event.  About simplest demo of an AESEnd */
  442. void SendSimple(void)
  443. {
  444.     AppleEvent ourEvent,ourReply;
  445.     short sendIt = 2;
  446.     AECreateAppleEvent(kSimpleClass, kSimpleEvent, &gTheAddress, kAutoGenerateReturnID, kAnyTransactionID, &ourEvent);
  447.     if (replyLevels[replyValue] == kAEWaitReply && !gSwitchLayer) {
  448.         sendIt = StopAlert(kBadCombo, nil);
  449.     }
  450.     if (sendIt == 2) {
  451.         AESend(&ourEvent, &ourReply, (gSendInteractArray[gInteract] + gSwitchLayer) + replyLevels[replyValue], kAENormalPriority,
  452.                kAEDefaultTimeout,gAEIdleUPP, nil);
  453.     }
  454.     AEDisposeDesc(&ourEvent);
  455.     if(replyLevels[replyValue] == kAEWaitReply)AEDisposeDesc(&ourReply);
  456. }
  457.  
  458. /* I'm just launching it, not searching for a port */
  459. /* I use PBCatSearch to search for it's creator and file type. */
  460. OSErr FindReciever(AEDesc *theAddress)
  461. {
  462.     OSErr myError;
  463.     DialogPtr search = GetNewDialog(kSearch,nil,(WindowPtr)-1);
  464.     CSParamPtr csBlockPtr = (CSParamPtr)NewPtrClear(sizeof(CSParam));
  465.     long dirIDUnused;
  466.     Str32 nulString = "\p";
  467.     /* initialize the parameter block */
  468.     DrawDialog(search);
  469.     if (csBlockPtr) {
  470.         csBlockPtr->ioSearchInfo1 = (CInfoPBPtr)NewPtrClear(sizeof(CInfoPBRec));
  471.         csBlockPtr->ioSearchInfo2 = (CInfoPBPtr)NewPtrClear(sizeof(CInfoPBRec));
  472.         if (csBlockPtr->ioSearchInfo1 && csBlockPtr->ioSearchInfo2) {
  473.             csBlockPtr->ioMatchPtr = (FSSpecPtr)NewPtrClear(sizeof(FSSpec) * 1);        /* only looking for 1 */
  474.             if (csBlockPtr->ioMatchPtr) {
  475.                 /* Now see if we can create an optimization buffer */
  476.                 csBlockPtr->ioOptBuffer = NewPtr(2048);
  477.                 if (csBlockPtr->ioOptBuffer)
  478.                     csBlockPtr->ioOptBufSize = 2048;
  479.                 else
  480.                     csBlockPtr->ioOptBufSize = 0;           /* no buffer, sorry */
  481.                 csBlockPtr->ioReqMatchCount = 1;
  482.                 csBlockPtr->ioSearchTime = 0;               /* no timeout */
  483.             }
  484.         }
  485.     }
  486.     HGetVol(nil, &csBlockPtr->ioVRefNum, &dirIDUnused);     /* get default volume for search */
  487.     csBlockPtr->ioSearchInfo1->hFileInfo.ioNamePtr = nil;
  488.     csBlockPtr->ioSearchInfo2->hFileInfo.ioNamePtr = nil;
  489.     csBlockPtr->ioSearchInfo1->hFileInfo.ioFlFndrInfo.fdCreator = 'MuuB';
  490.     csBlockPtr->ioSearchInfo1->hFileInfo.ioFlFndrInfo.fdType = 'APPL';
  491.     csBlockPtr->ioSearchBits = fsSBFlFndrInfo;
  492.     csBlockPtr->ioSearchInfo2->hFileInfo.ioFlFndrInfo.fdCreator = 0xFFFFFFFF;
  493.     csBlockPtr->ioSearchInfo2->hFileInfo.ioFlFndrInfo.fdType = 0xFFFFFFFF;
  494.     
  495.     myError = PBCatSearch(csBlockPtr, false);               /* search sync */
  496.     if (myError == noErr && csBlockPtr->ioActMatchCount != 0) {
  497.         /* we found it, so launch it */
  498.         
  499.         launchThis.launchBlockID = extendedBlock;
  500.         launchThis.launchEPBLength = extendedBlockLen;
  501.         launchThis.launchFileFlags = nil;
  502.         launchThis.launchControlFlags = launchContinue + launchNoFileFlags + launchDontSwitch;
  503.         launchThis.launchAppSpec = &csBlockPtr->ioMatchPtr[0];
  504.         myError = LaunchApplication(&launchThis);
  505.         if (myError == noErr) {
  506.             /* it launched fine.  we can use the PSN to make a target */
  507.             AECreateDesc(typeProcessSerialNumber, (Ptr)&launchThis.launchProcessSN, sizeof(ProcessSerialNumber), theAddress);
  508.             
  509.         }
  510.     }
  511.     
  512.     /* no matter what happened, kill the memory we had allocated */
  513.     if (csBlockPtr) {
  514.         if (csBlockPtr->ioSearchInfo1)
  515.             DisposePtr((Ptr)csBlockPtr->ioSearchInfo1);
  516.         if (csBlockPtr->ioSearchInfo2)
  517.             DisposePtr((Ptr)csBlockPtr->ioSearchInfo2);
  518.         if (csBlockPtr->ioMatchPtr)
  519.             DisposePtr((Ptr)csBlockPtr->ioMatchPtr);
  520.         if (csBlockPtr->ioOptBuffer)
  521.             DisposePtr((Ptr)csBlockPtr->ioOptBuffer);
  522.         DisposePtr((Ptr)csBlockPtr);
  523.     }
  524.     
  525.     /* catsearch section end */
  526.     DisposeDialog(search);
  527.     return(myError);
  528. }
  529. /* My IdleProc for AESend */
  530. pascal Boolean idleProc(EventRecord *eventIn, long *sleep, RgnHandle *mouseRgn)
  531. {
  532.     switch (eventIn->what) {
  533.         case nullEvent:
  534.             /* no nul processing in this sample */
  535.             *sleep = 0;
  536.             mouseRgn = nil;
  537.             break;
  538.         case updateEvt:
  539.         case activateEvt:
  540.             DrawMain((WindowPtr)eventIn->message);          /* draw whatever window needs an update */
  541.             break;
  542.         case app4Evt:
  543.             switch ((gERecord.message >> 24) & 0x0FF) {     /* high byte of message */
  544.                 case suspendResumeMessage:                  /* suspend/resume is also an activate/deactivate */
  545.                     gInBackground = (gERecord.message & kResumeMask) == 0;
  546.                     break;
  547.             }
  548.             break;
  549.             
  550.             
  551.             
  552.     }
  553. return(false);    /* I'll wait forever */
  554. }
  555.